package com.shisan.tingshu.payment.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.shisan.tingshu.common.rabbit.constant.MqConst;
import com.shisan.tingshu.common.rabbit.service.RabbitService;
import com.shisan.tingshu.common.result.Result;
import com.shisan.tingshu.common.util.AuthContextHolder;
import com.shisan.tingshu.model.payment.PaymentInfo;
import com.shisan.tingshu.payment.config.WxPayV3Config;
import com.shisan.tingshu.payment.mapper.PaymentInfoMapper;
import com.shisan.tingshu.payment.service.PaymentInfoService;
import com.shisan.tingshu.payment.service.WxPayService;
import com.shisan.tingshu.payment.util.PayUtil;
import com.shisan.tingshu.user.client.UserInfoFeignClient;
import com.shisan.tingshu.vo.user.UserInfoVo;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

	@Autowired
	private PaymentInfoService paymentInfoService;

	@Autowired
	private PaymentInfoMapper paymentInfoMapper;

	@Autowired
	private WxPayV3Config wxPayV3Config;

	@Autowired
	private UserInfoFeignClient userInfoFeignClient;

	@Autowired
	private RabbitService rabbitService;

	@Autowired
	private StringRedisTemplate redisTemplate;

	@Override
	public Map<String, Object> createJsapi(String wxPayItemType, String orderNo) {

		// 0.payment_info表中插入订单支付流水
		PaymentInfo paymentInfo = paymentInfoService.savePaymentInfo(wxPayItemType, orderNo);
		// 1.构建config对象
		RSAAutoCertificateConfig config = wxPayV3Config.getConfig();
		// 2.构建service再在利用config对象
		JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
		// 3.调用预支付下单接口，以及获取微信支付的必要参数
		PrepayRequest request = new PrepayRequest();
		// 4.构建请求参数
		Amount amount = new Amount();// 账户对象
		amount.setTotal(1); // （测试数据：1分钱）TODO：生产环境下要根据订单编号去订单数据库中查询这个商品的实际金额
		request.setAmount(amount);  // 购买商品的总金额
		request.setAppid(wxPayV3Config.getAppid());  // appid
		request.setMchid(wxPayV3Config.getMerchantId()); // 商户id
		request.setDescription("买了一个飞机");  // 商品内容 TODO：生产环境下要根据订单编号去订单数据库中查询这个商品的名字（标题）
		request.setNotifyUrl(wxPayV3Config.getNotifyUrl());  // 异步回调地址
		request.setOutTradeNo(orderNo);// 给微信平台的订单编号
		Payer payer = new Payer();  // 构建付款者对象
		Result<UserInfoVo> userInfo = userInfoFeignClient.getUserInfo(AuthContextHolder.getUserId());
		UserInfoVo userInfoData = userInfo.getData();
		Assert.notNull(userInfoData, "用户信息不存在");
		payer.setOpenid(userInfoData.getWxOpenId()); // 付款者的openId
		request.setPayer(payer);

		// 5.发起请求 或者微信支付的必要参数
		// response包含了调起支付所需的所有参数，可直接用于前端调起支付
		PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);

		// 6.解析出来微信支付必要参数
		String packageVal = response.getPackageVal();  // 会话标识
		String timeStamp = response.getTimeStamp(); // 时间戳
		String nonceStr = response.getNonceStr(); // 随机字符串
		String signType = response.getSignType(); // 默认RSA，仅支持RSA
		String paySign = response.getPaySign(); // 签名算法生成的签名值

		// 7.返回前端
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("timeStamp", timeStamp);
		map.put("nonceStr", nonceStr);
		map.put("package", packageVal);
		map.put("signType", signType);
		map.put("paySign", paySign);

		return map;
	}

	/**
	 * 微信支付成功后做的事
	 * @param orderNo
	 * @param transaction
	 */
	@Override
	public void paySuccess(String orderNo, Transaction transaction) {

		// 1.将payment_info表中的支付状态修改为已支付
		int count = paymentInfoMapper.updatePaymentInfoStatus(orderNo);
		if (count > 0) {
			log.info("修改订单的支付状态成功");
		} else {
			log.error("修改订单的支付状态失败");
		}
		// 2.查询该笔订单的支付类型
		PaymentInfo paymentInfo = paymentInfoMapper.selectOne(new LambdaQueryWrapper<PaymentInfo>().eq(PaymentInfo::getOrderNo, orderNo));
		String paymentType = paymentInfo.getPaymentType();
		String routingKey = "1301".equals(paymentType) ? MqConst.ROUTING_ORDER_PAY_SUCCESS : MqConst.ROUTING_RECHARGE_PAY_SUCCESS;
		Map<String, String> msgMap = new HashMap<>();
		msgMap.put("orderNo", orderNo);
		String openid = transaction.getPayer().getOpenid();
		Result<String> userIdResult = userInfoFeignClient.getUserIdByOpenId(openid);
		String userId = userIdResult.getData();
		msgMap.put("userId", userId);
		String repeatQueryStatusKey = "order:pay:query:lock:" + orderNo;
		Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(repeatQueryStatusKey, "1", 1, TimeUnit.MINUTES);
		if (aBoolean) {
			rabbitService.sendMessage(MqConst.EXCHANGE_ORDER, routingKey, JSONObject.toJSONString(msgMap));
		}
	}

	/**
	 * 异步通知支付结果
	 * @param httpServletRequest
	 * @return
	 */
	@Override
	public Transaction asyncNotify(HttpServletRequest httpServletRequest) {

		// 主要做两件事情：
		// 1.验签：验证数据是不是微信平台回调过来的
		// 2.解析：解析密文，将其转成可用的明文

		// 1.从原始请求的请求头中获取验签和解析要用到的参数
		String signature = httpServletRequest.getHeader("Wechatpay-Signature"); // 微信回调时的签名
		String serial = httpServletRequest.getHeader("Wechatpay-Serial"); // 微信平台证书的序列化
		String nonce = httpServletRequest.getHeader("Wechatpay-Nonce"); // 签名中的随机数
		String timestamp = httpServletRequest.getHeader("Wechatpay-Timestamp"); // 签名中的时间戳
		// 2.初始化 NotificationParser
		NotificationParser parser = new NotificationParser(wxPayV3Config.getConfig());
		String originContent = PayUtil.readData(httpServletRequest);  // 获取微信异步传过来的原始报文
		// 3.构建请求参数对象
		RequestParam requestParam = new RequestParam.Builder()
				.serialNumber(serial)
				.nonce(nonce)
				.signature(signature)
				.timestamp(timestamp)
				.body(originContent)
				.build();
		try {
			// 4.解析（验签>>解密>>转换成回调需要用到的Transaction对象）
			Transaction transaction = parser.parse(requestParam, Transaction.class);
			return transaction;
		} catch (ValidationException e) {
			log.error("签名验证失败", e.getMessage());
			return null;
		}
	}
}
